/**
 * @File name: driver/serial.c
 * @Synopsis:
 * @Author: xing.dai@aliyun.com
 * @Version: 0.0.1
 * @Date: 2018-08-30 18:23
 */

#include "serial.h"


static int serial_init(device_t *device)
{
    int res = SUCC;
    serial_device_t *serial;

    assert(device != NULL);
    serial = (serial_device_t *)device;

    /* initilize rx/tx */
    serial->serial_rx = NULL;
    serial->serial_tx = NULL;

    /* Apply configuration */
    if (serial->ops->configure)
        res = serial->ops->configure(serial, &serial->config);

    return res;
}
static int serial_open(struct _device *device, uint32 oflag)
{
    return 0;
}
static int serial_close(struct _device *device)
{
    return 0;
}
static inline int _serial_pool_rx(serial_device_t *serial, char *data, int32 length)
{
    int ch;
    int size;
    assert(serial != NULL);
    size = length;

    while (length) {
        ch = serial->ops->getc(serial);
        if (ch == -1)
            break;
        *data = ch;
        data++;
        length--;
        if (ch == '\n' || ch == '\r')
            break;
    }
    return size - length;
}

static uint32 serial_read(struct _device *device, int32 pos, void *buffer, int32 length)
{
    serial_device_t *serial;

    assert(device != NULL);
    if (length == 0)
        return 0;
    serial = (serial_device_t *)device;
    return _serial_pool_rx(serial, (char *)buffer, length);
}


static inline int _serial_pool_tx(serial_device_t *serial, char *data, int32 length)
{
    int32 size;
    assert(serial != NULL);

    size = length;
    while (length) {
        if (*data == '\0')
            break;
        if (*data == '\n')
            serial->ops->putc(serial, '\r');
        serial->ops->putc(serial, *data);
        if (*data == '\r' && *(data + 1) != '\n')
            serial->ops->putc(serial, '\n');
        data++;
        length--;
    }
    return size - length;
}

static uint32 serial_write(struct _device *device, int32 pos, void *buffer, int32 length)
{
    serial_device_t *serial;

    assert(device != NULL);
    if (length == 0)
        return 0;
    serial = (serial_device_t *)device;
    return _serial_pool_tx(serial, (char *)buffer, length);
}

static int serial_control(struct _device *device, uint8 cmd, void *args)
{
    return 0;
}

int hw_serial_register(serial_device_t *serial, char *name, uint32 flag, void *data)
{
    device_t *device;
    assert(serial != NULL);

    device = (device_t *)serial;

    device->type = DEVICE_CLASS_CHAR;
    device->rx_indicate = NULL;
    device->tx_complete = NULL;

    device->init	= serial_init;
    device->open	= serial_open;
    device->close	= serial_close;
    device->read	= serial_read;
    device->write	= serial_write;
    device->control = serial_control;
    device->private = data;

    return device_register(device, name, flag);
}
